﻿//main.cpp
#include <iostream>
#include <stdio.h>
#include <cstddef>
#include "base64.h"
#include "sha1.h"

#include "Socket.h"

using namespace std;

struct ServerInfo
{
	char* ip;//服务ip
	int port;//服务端口
	char* srcfile;//静态页面文件
	char* wsurl;//WebSocket地址
	char* httpurl;//静态页面地址
	int second;//等待时间
};

//定义handshake结构体变量

/*
		GET /chat HTTP/1.1
		Host: server.example.com
		Upgrade: websocket
		Connection: Upgrade
		Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
		Origin: http://example.com
		Sec-WebSocket-Protocol: chat, superchat
		Sec-WebSocket-Version: 13
*/

struct handshake
{
	char *get;
	char *host;
	char *key;
	char *origin;
	char *protocol;
	char *version;
};

void server(ServerInfo& sv);

char* parse_msg( char *);
char* build_msg( char *message, int length);

//释放握手后不再使用的部分变量

void free_handshake(struct handshake* hs)
{
	if( hs->get != NULL )            free(hs->get);
	if( hs->host != NULL )           free(hs->host);
	if( hs->key != NULL )            free(hs->key);
	if( hs->protocol != NULL )       free(hs->protocol);
	if( hs->version != NULL )        free(hs->version);
}

char* match_string(const char* src, const char* pattern, char end)
{
	char buf[1024];
	memset(buf, 0, 1024);
	size_t src_len = strlen(src); 
	size_t ptn_len = strlen(pattern);
	unsigned short b=0, p=0, i=0; 
	char c='\0';
	for(i=0; i<src_len; i++)
	{
		c = src[i];
		if(p==ptn_len)
		{ // p==ptn_len 表示正在匹配中
			if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) 
			{
				p++; // 匹配结束
			} else
			{
				buf[b++]=c; // 匹配到的字符 
			}
		}else if(p<ptn_len)
		{    // 为达到匹配要求
			if(c==pattern[p])
			{
				p++;
			}
			else 
			{
				p=0;
			}
		}
	}
	size_t ret_len = strlen(buf);
	char *ret_p; 
	if( ret_len>0 )
	{
		ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
		memcpy(ret_p, buf, ret_len);
	} else 
	{
		ret_p = NULL;
	}
	return ret_p; 
}

void handshake_test(const char* src, struct handshake* hs)
{
	hs->get = match_string(src, "GET ", 0x20); // 提取空格之前
	hs->host = match_string(src, "Host: ", '\0');
	hs->key = match_string(src, "Sec-WebSocket-Key: ", '\0');	
	hs->protocol = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
	hs->origin = match_string(src, "Origin: ",'\0');
	hs->version = match_string(src,"Sec-WebSocket-Version: ",'\0');
}

DWORD WINAPI ThreadWebSocket(LPVOID p);

int main(int argc, char* argv[])
{
	ServerInfo info;
	info.ip="127.0.0.1";
	info.port=8089;
	info.httpurl="/";
	info.wsurl="/chat";//新版的websocket客户请求
	info.srcfile=NULL;
	info.second=10;
	
	for (int i=1;i<argc; i++)
	{
		if (argv[i][0]=='-')
		{
			switch (argv[i][1])
			{
			case 'i': 
				info.ip=argv[++i];
				break;
			case 'p': 
				info.port=atoi(argv[++i]);
				break;
			case 'w': 
				info.wsurl= argv[++i];
				break;
			case 'u':				
				info.httpurl= argv[++i];
				break;
			case 'f': 
				info.srcfile= argv[++i];
				break;
			}
			
		}
		
	}
	
	if (argc==1)
	{
		cout<<"usage"<<endl;//[-ip 127.0.0.1]
		cout<<"\tWebSocket [-port 6666] [-wsurl /ws] [-url /] [-file .\1.htm]"<<endl;
		cout<<"now is listen on 6666"<<endl;
		cout <<"use chrome visit http://localhost:6666/"<<endl;
	}
	
	server(info);
	
	return 0;
}

void server(ServerInfo& sv)
{
	CSocket socket;	
	bool ret = socket.Listen(sv.ip,sv.port);

	cout<<"ret"<<endl;
	if (!ret)
	{
		cout<<"侦听失败！！"<<endl;
		return;
	}

	while (true)
	{
		cout<< "进入循环"<<endl;
		CSocket* pc= socket.Accept();
		
		if (pc!=NULL)
		{		
			
			char buff[2048];
			int len = pc->Recv(buff,2048);
			
			//cout << ("len ") << len<<endl << "出错代码：" << WSAGetLastError() << endl << "出错文件：" << __FILE__ << endl << "出错行数：" << __LINE__ << endl;
			if (len<0)
			{
				cout<<"error"<<endl;
			}
			
			//buff[len+1]='\0';
			//cout<< buff<<endl;
			if(len > 0)
			{
				//GET/
				//if (strncmp(buff+4,sv.wsurl,strlen(sv.wsurl))==0&& buff[4+strlen(sv.wsurl)]==' ')
				{
					//send handshake
					//char* pHandshake = "HTTP/1.1 101 Switching Protocols\r\n"
					//"Upgrade: WebSocket\r\n"
					//"Connection: Upgrade\r\n"				
					//"Sec-WebSocket-Accept: ws://localhost:%d%s\r\n\r\n";
					//char head[1024]={0};
					//sprintf_s(head,1024,pHandshake,sv.port, sv.port,sv.wsurl);
					//sprintf_s(head,1024,pHandshake);
					
					struct handshake hs = {NULL,NULL,NULL,NULL,NULL,NULL};
					handshake_test(buff,&hs);				
					string value= hs.key;
					value+="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
					cout << "value: "<< value <<endl;
					//key convert
					SHA1 sha;
					unsigned int message_digest[5];

					sha.Reset();
					sha << value.c_str();

					sha.Result(message_digest);
					// convert sha1 hash bytes to network byte order because this sha1
					//  library works on ints rather than bytes
					for (int i = 0; i < 5; i++) {
						message_digest[i] = htonl(message_digest[i]);
					}

					value = base64_encode(
					reinterpret_cast<const unsigned char*>
					(message_digest),20
					);
					
					cout << "vlaue after convert:" << value <<endl;
					char* res=new char[1024];
					strcpy(res,value.c_str());
					
					char* buffer=new char[1024];
					
					sprintf(buffer,"HTTP/1.1 101 Switching Protocols\r\n"
					"Upgrade: WebSocket\r\n"
					"Connection: Upgrade\r\n"
					"Sec-WebSocket-Accept: %s\r\n\r\n",res);
					
					cout << buffer << endl;
					free_handshake(&hs);

					//pc->Send(head,(int )strlen(head));
					pc->Send(buffer,(int )strlen(buffer));
					
					//start a thread for web socket
					CreateThread(0,0,ThreadWebSocket,pc,0,0);
				}
				/* else if (strncmp(buff+4,sv.httpurl,strlen(sv.httpurl))== 0 && buff[4+strlen(sv.httpurl)] == ' ')
			{
				char* pSendHead = "HTTP/1.1 200 OK\r\n"
				"Content-Length: %d\r\n\r\n";
				char* pHtmlData="\r\n";
				
				char* pToHtml = pHtmlData;
				char* pData = NULL;
				//send file or default data
				if (sv.srcfile)
				{
					FILE* fp=NULL;
					int st= fopen_s(&fp, sv.srcfile, "rb");
					
					if (fp)
					{
						fseek(fp,0, SEEK_END);
						int filelen= ftell(fp);
						fseek(fp, 0, SEEK_SET);
						pData = new char[filelen+1];
						fread(pData, filelen, 1, fp);
						fclose(fp);
						pData[filelen]=0;
						pToHtml = pData;
					}
					
				}
				char html[1024]={0};
				char head[1024]={0};
				
				if (pToHtml == pHtmlData)
				{
					sprintf_s(html,1024,pHtmlData,sv.port,sv.wsurl);
					pToHtml=html;
				}
				sprintf_s(head,1024,pSendHead,strlen(pToHtml));
				
				pc->Send(head,(int)strlen(head));
				pc->Send(pToHtml,(int)strlen(pToHtml));
				
				pc->Close();
				delete pc;
				pc=NULL;
				if (pData)
				{
					delete pData;
					pData= NULL;
				}
				
			}
			else
			{
				//send 404 not found
				char* pNotFound ="HTTP/1.1 404 NotFound\r\n"
				"Content-Length: 0"
				"\r\n\r\n";
				pc->Send(pNotFound,(int)strlen(pNotFound));
				pc->Close();
				delete pc;
				pc =NULL;
			} */
				
			}
		}
	}

}

DWORD WINAPI ThreadWebSocket(LPVOID p)
{
	char buff[2048];
	char *message;
	CSocket* pc = (CSocket*)p;
	while (true)
	{
		int len = pc->Recv(buff,2048);
		if (len >0)
		{
			if(buff[0]!=(char)0x88){
				
				cout << "len:"<< len<< endl;
				
				char* q=buff;
				for(int i=0;i<len;i++,q++)
				{
					for (int m=7;m>=0;m--)
					{
						unsigned char a=0;
						unsigned char b=0x01;
						a=( (*q)>>m )&b; /*可见内循环*q的值是不变的 */
						printf("%u",a);
					}
					cout<<" ";
				}
				
				message=parse_msg(buff);
				cout <<endl<<"Recv data(message): " << message <<endl;
				
				pc->Send(build_msg("qwertyuioplkjhgfdsazxcvbnmqwertyuioplkjhgfdsazxcvbnmqwertyuioplkjhgfdsazxcvbnmqwertyuioplkjhgfdsazxcvbnmqwertyuioplkjhgfdsazxcvbnm",131),131+4);
			}
			else{
				cout<<"关闭一个客户端 "<<endl;
				pc->Close();
				delete pc;
				pc = NULL;
				return 0;
			}
		}
		else
		{
			break;
		}
		
	}
	pc->Close();
	delete pc;
	pc = NULL;
	return 0;
}

//解包
char* parse_msg( char *buff)
{
	char *message=new char[2048];
	if(buff[1]&0x80)
	{
		int mlen=buff[1]&0x7F;//payload len，数据长度
		
		if (mlen<126)
		{
			cout<< endl << "mlen:"<< mlen<< endl;
			for (int i=0;i<mlen;i++)
			{
				message[i]=buff[6+i]^buff[i%4+2];//第3 4 5 6字节为掩码，第7个字节开始为数据
			}
			message[mlen]='\0';
		}
		else if(mlen==126)
		{
			mlen=(unsigned char)buff[2]*256+(unsigned char)buff[3];//实际数据长度，占2字节
			cout<< endl << "mlen:"<< mlen<< endl;
			for (int i=0;i<mlen;i++)
			{
				message[i]=buff[8+i]^buff[i%4+4];//第5 6 7 8字节为掩码，第9个字节开始为数据
			}
			message[mlen]='\0';
		}
	}
	return message;
}

//打包，无掩码
char* build_msg( char *message, int length)
{
	char *buff=new char[2048];
	buff[0]=0x81;//第一个字节
	if (length<126)
	{
		buff[1]=length;		
		for (int i=0;i<length;i++)
		{
			buff[2+i]=message[i];//第3个字节开始存数据
		}
	}
	else if (length<65535)
	{
		buff[1]=126;
		buff[2]=(length>>8)&0xFF;
		buff[3]=length&0xFF;//第3 4个字节存数据长度
		for (int i=0 ;i<length;i++)
		{
			buff[4+i]=message[i];//第5个字节开始存数据
		}
	}
	
	return buff;
}
